package ssl

import (
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"gitee.com/yysf-xin/dproxy/pkg/tools"
	"github.com/bluele/gcache"
	"hash/crc64"
	"math/big"
	"time"
)

type CA struct {
	*tls.Certificate
	crc   *crc64.Table
	cache gcache.Cache
}

func NewCa(crt, key string) (ca *CA, err error) {
	c, err := tls.LoadX509KeyPair(crt, key)
	if err != nil {
		return
	}
	c.Leaf, err = x509.ParseCertificate(c.Certificate[0])
	if err != nil {
		tools.Log.Println("parse Certificate fail", key)
		return
	}
	ca = &CA{
		&c,
		crc64.MakeTable(crc64.ECMA),
		nil,
	}
	ca.cache = gcache.New(100).ARC().Expiration(1 * time.Hour).LoaderFunc(func(key interface{}) (interface{}, error) {
		return ca.CreateCert(key.(string))
	}).Build()
	return
}

func (ca *CA) Read(p []byte) (n int, err error) {
	for i := 0; i < len(p); i++ {
		p[i] = byte(i)
	}
	return len(p), nil
}
func (ca *CA) DummyCert(commonName string) (*tls.Certificate, error) {
	v, err := ca.cache.Get(commonName)
	if err != nil {
		return nil, err
	}
	return v.(*tls.Certificate), err
}
func (ca *CA) CreateCert(commonName string) (*tls.Certificate, error) {

	serial := int64(crc64.Checksum(tools.StringToBytes(commonName), ca.crc))
	if serial < 0 {
		serial = -serial
	}
	template := &x509.Certificate{
		SerialNumber: big.NewInt(serial),
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: ca.Leaf.Subject.Organization,
		},
		NotBefore:          time.Now().Add(-time.Hour * 48),
		NotAfter:           time.Now().Add(time.Hour * 24 * 365),
		SignatureAlgorithm: x509.SHA256WithRSA,
		ExtKeyUsage:        []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
		DNSNames:           []string{commonName},
	}

	certBytes, err := x509.CreateCertificate(ca, template, ca.Leaf, ca.Leaf.PublicKey,
		ca.PrivateKey)

	if err != nil {
		return nil, err
	}

	cert := &tls.Certificate{
		Certificate: [][]byte{certBytes},
		PrivateKey:  ca.PrivateKey,
	}
	return cert, nil
}
